import http from 'node:http'
import fs from 'node:fs'
import logger from './logger.js'
import NodeURL from 'node:url'
import { ERROR_CODE, MQTT_TOPIC } from '../../constant.js'
import path from 'node:path'
import mqttClient from '../mqtt/index.js'

http.ServerResponse.prototype.json = function(object) {
    try {
        const jsonStr = JSON.stringify(object)
        this.setHeader('Content-Type', 'application/json')
        this.end(jsonStr)
    } catch (error) {
        logger.error(error)
    }
}
http.ServerResponse.prototype.status = function(code) {
    this.statusCode = code
    return this
}
http.IncomingMessage.prototype.query = function() {
    return NodeURL.parse(this.url, true).query
}
http.IncomingMessage.prototype.body = function() {
    return new Promise((resolve) => {
        let body = ''
        this.on('data', (chunk) => { body += chunk.toString() })
        this.on('end', () => {
            const bodyJson = (() => {
                try {
                    return JSON.parse(body)
                } catch {
                    return body
                }
            })()
            resolve(bodyJson ?? {})
        })
    })
}

let systemInit = false

export class HttpServer {
    constructor(props) {
        const {
            port = 8080,
            hostname = '127.0.0.1',
            listeningHandler,
            router
        } = props
        this.router = router
        this.port = port
        this.hostname = hostname
        this.listeningHandler = listeningHandler
        this.server = this.initHttpServer()
    }

    initHttpServer() {
        logger.info('System initializing, please wait!')
        let leftSecond = 10
        const interval = setInterval(() => {
            leftSecond--
            logger.info(`Time remaining: ${leftSecond} seconds`)
            if (leftSecond === 0) {
                clearInterval(interval)
                systemInit = true
                logger.info('System initialization completed!')
                // 系统上线，发布 mqtt 消息, topic: '/system/availability', data: { online: true }
                mqttClient.publish(MQTT_TOPIC.SYSTEM_AVAILABILITY, JSON.stringify({ online: true }), {
                    qos: 2,
                    retain: true
                }, (error) => {
                    if (error) {
                        logger.error(`[MQTT] publish topic: ${MQTT_TOPIC.SYSTEM_AVAILABILITY} error`, error)
                    } else {
                        logger.info(`[MQTT] publish topic: ${MQTT_TOPIC.SYSTEM_AVAILABILITY} success`)
                    }
                })
            }
        }, 1000)
        
        const server = http.createServer(async (request, response) => {
            const { method, url } = request
            const { pathname } = NodeURL.parse(url)
            logger.info(`[${method}] ${url} request coming`)

            if (!systemInit) {
                logger.info('System is initializing, please wait!')
                response.json({ error: ERROR_CODE.SYSTEM_INITIALIZING })
                return
            }

            try {
                let mqttConnected = false
                if (mqttConnected) {
                    response.json({ error: ERROR_CODE.SYSTEM_INITIALIZING, message: 'system initializing!' })
                    return
                }

                // 静态文件目录
                if (pathname.startsWith('/static')) {
                    logger.info(`request for static file: ${pathname}`)
                    try {
                        const filepath = path.join(process.cwd(), pathname)
                        const stat = fs.statSync(filepath)
                        if (stat.isFile()) {
                            fs.createReadStream(filepath).pipe(response);
                        } else {
                            fs.createReadStream(`${filepath}/index.html`).pipe(response);
                        }
                    } catch (error) {
                        logger.info(error)
                        response.status(404).end(http.STATUS_CODES[404]);
                    }
                    return
                }

                // 路由处理
                const handler = this.router.getRouteHandler(method, pathname)
                if (handler) {
                    const data = await handler(request, response, this.server)
                    if (data) {
                        logger.info(`[${method}] ${url} response: ${JSON.stringify(data)}`)
                        response.json(data)
                    }
                } else {
                    const code = 404
                    response.status(code).json({
                        data: { url, method },
                        error: ERROR_CODE.NOT_FOUND,
                        message: http.STATUS_CODES[code]
                    })
                    logger.info(`route ${code}: method: ${method}; route: ${url}`)
                }
            } catch (error) {
                const code = 500
                response.status(code).json({
                    error: code,
                    message: error.message || http.STATUS_CODES[code]
                })
                logger.error(error)
            }
        })

        server.on('close', () => {
            logger.info('server close!')
        })

        server.listen(this.port, this.hostname, this.listeningHandler ?? (() => {
            logger.info(`server start on ${this.hostname}:${this.port}`)
        }))
        
        return server
    }
}
